package online.chazi.netkit.messaging;

import io.netty.channel.ChannelFutureListener;
import lombok.extern.slf4j.Slf4j;
import online.chazi.netkit.NetkitContext;
import online.chazi.netkit.Session;
import online.chazi.netkit.action.Action;
import online.chazi.netkit.action.ActionChain;
import online.chazi.netkit.action.ActionFilter;
import online.chazi.netkit.exception.ActionException;
import org.apache.commons.lang.reflect.ConstructorUtils;

import java.io.Serializable;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 说明：作者很懒，什么都没留下
 * Created by 叉子同学 on 2020-04-24 16:12
 */
@Slf4j
public class MessageDispatcher {

    private Map<Serializable, Map<Integer, Action>> cachedActions = new ConcurrentHashMap<>();
    private ActionChain chain = new ActionChain();

    public void dispatch(Session session, Message message) {
        this.dispatch(session.getContext(), session, message);
    }

    public void dispatch(NetkitContext context, Session session, Message message) {
        int messageID = message.getMessageId();

        try {
            Action action = this.getAction(context, session, messageID);
            if (action == null) {
                return;
            }

            action.setSession(session);
            Iterator<ActionFilter> it = null;
            List<ActionFilter> list = context.getActionFilterList();
            if (list != null) {
                it = list.iterator();
            }

            this.executeAction(it, action, message);
        } catch (Exception e) {
            log.error(e.getMessage(), e);
        }

    }

    private Action getAction(NetkitContext context, Session session, int messageId) throws Exception {

        Map<Integer, Action> actionMap = null;
        if (context.isUsingActionCached()) {
            actionMap = cachedActions.get(session.getChannel().id());
            if (actionMap == null) {
                actionMap = new HashMap<>();
                cachedActions.put(session.getChannel().id(), actionMap);
                session.getChannel().closeFuture().addListener((ChannelFutureListener) channelFuture -> removeCacheAction(channelFuture.channel().id()));
            } else {
                Action action = actionMap.get(messageId);
                if (action != null) return action;
            }
        }

        Action action;
        Class<? extends Action> actionClass = context.lookupRegisteredActionClass(messageId);
        if (actionClass == null) {
            action = context.getNotFoundAction();
        } else {
            action = (Action) ConstructorUtils.invokeConstructor(actionClass, null);
        }
        if (action != null) {
            context.getInjectProxy().inject(action);
            if (context.isUsingActionCached() && actionMap != null) {
                actionMap.put(messageId, action);
            }
        }
        return action;

    }

    private void removeCacheAction(Serializable s) {
        log.debug("清除cache action.");
        cachedActions.remove(s);
    }

    private synchronized void executeAction(Iterator<ActionFilter> it, Action action, Message message) throws ActionException {
        this.chain.setIterator(it);
        this.chain.doChain(action, message);
    }

}
